#include <iostream>
#include <fstream>

#include "topdbs_snowflake.hpp"
#include "topdbs_connection.hpp"
#include "topdbs_factory.hpp"
#include "topdbs_encrypt.hpp"

namespace topdbs
{
    static string topdbs_decrypt_passwd(const string& pass)
    {
        char decPass[256] = {0};
        topdbs_encrypt_util::PasswdDec(pass.c_str(),decPass);
        return string(decPass); 
    }

    SQLConnection::SQLConnection()
        : m_pConnection(nullptr)
        , m_dbsClient(topdbs_sqlite)
        , m_dbsLocktime(0)
        , m_dbsEncrypt(false)
        , m_dbsConnectionId(topdbs_snowflake::generator_id())
    {
    }

    SQLConnection::~SQLConnection()
    {
        if(m_pConnection)
        {
            delete m_pConnection;
            m_pConnection = nullptr;
        }
    };

    eDBSClient_t  SQLConnection::getDbsCient()
    {
        return m_dbsClient;
    }

    std::string SQLConnection::getDbsVersion()
    {
        return TOPDBS_VERSION;
    }

    topdbs_base_connection* SQLConnection::getConnection()
    {
        return m_pConnection;
    }

    uint64_t SQLConnection::getConnectionID() const 
    { 
        return m_dbsConnectionId ; 
    };

    void SQLConnection::setClientEncode(const string& encode)
    {
        m_dbsCharset = encode;
    }

    void SQLConnection::setLocktime(uint16_t locktime) 
    { 
        m_dbsLocktime = locktime;
    }; 

    void SQLConnection::setEncrypt(bool on)
    {
        m_dbsEncrypt = on;
    }   

    int SQLConnection::connect(eDBSClient_t dbsClient,
        const string& dbString,const string& user,const string& pass,void* pOption )
    {
        int iRet = 0;
        dbslog(DBS_DEBUG,"=====SQLConnection Connect BEGIN======");
        m_dbsClient = dbsClient;
        topdbs_connection_factory factory(dbsClient);
        m_pConnection = factory.create();
        if(!m_pConnection)
        {
            return ERR_DBS_FAIL;
        }
        std::string decrypt_pass = m_dbsEncrypt ? topdbs_decrypt_passwd(pass) : pass;

        dbslog(DBS_TRACE,"dbsClient=%s",topdbs_client_desc(dbsClient));
        dbslog(DBS_TRACE,"user=%s",user.c_str());
        dbslog(DBS_TRACE,"dbsConnStr=%s",dbString.c_str());
        dbslog(DBS_TRACE,"pass=%s",pass.c_str());
        dbslog(DBS_TRACE,"decrypt_pass=%s",decrypt_pass.c_str());
        dbslog(DBS_TRACE,"locktime=%d",m_dbsLocktime);

        m_pConnection->setClientCharset(m_dbsCharset);
        m_pConnection->setLockwaitTime(m_dbsLocktime);
        iRet = m_pConnection->connect(m_dbsClient,dbString,user,decrypt_pass,pOption);
        if(iRet != ERR_DBS_OK)
        {
            dbslog(DBS_ERROR,"dbsconnect error:%s",m_pConnection->getErrors().c_str());
            std::cout << "dbsconnect failed." << m_pConnection->getErrors() << endl;
        }
        dbslog(DBS_DEBUG,"=====SQLConnection Connect END======");
        return iRet;
    };

    int   SQLConnection::connect(const string& dbsfile,const string& dbsgroup)
    {
        DBSConnectionParams dbsConfig;
        if( readDbsfile(dbsfile,dbsConfig,dbsgroup) != 0)
        {
            dbslog(DBS_ERROR,"dbsconnect error:read dbsfile failed!!!");
            std::cout << "dbsconnect failed:read dbsfile failed!!" << endl;            
            return ERR_DBS_FAIL;
        }
        m_dbsClient = dbsConfig.dbsType;
        setClientEncode(dbsConfig.dbsCharset);
        setLocktime(dbsConfig.dbsLocktime);
        m_dbsEncrypt = dbsConfig.dbsEncrypt;
        return connect(dbsConfig.dbsType,dbsConfig.dbsConnStr,dbsConfig.dbsUser,dbsConfig.dbsPass);
    }

    bool SQLConnection::isConnected()
    {
        return m_pConnection ? m_pConnection->isConnected() : false;
    };

    int SQLConnection::reConnect()
    {
        return m_pConnection ? m_pConnection->reConnect() : ERR_DBS_FAIL;
    };

    int SQLConnection::disConnect()
    {
        return m_pConnection ? m_pConnection->disConnect() : ERR_DBS_FAIL;
    };	

    int  SQLConnection::begin()
    {
        return m_pConnection ? m_pConnection->begin() : ERR_DBS_FAIL;
    };

    int  SQLConnection::commit()
    {
        return m_pConnection ? m_pConnection->commit() : ERR_DBS_FAIL;
    };

    int  SQLConnection::rollback()
    {
        return m_pConnection ? m_pConnection->rollback() : ERR_DBS_FAIL;
    };

    bool SQLConnection::isTransaction()
    {
        return m_pConnection ? m_pConnection->isTransaction() : false;
    };

    int  SQLConnection::autoCommit(bool on)
    {
        return m_pConnection ? m_pConnection->autoCommit() : ERR_DBS_FAIL;
    };	

    std::string SQLConnection::getErrors()
    {
        return m_pConnection ? m_pConnection->getErrors() : string();
    }

    std::string SQLConnection::getSqlcode()
    {
        return m_pConnection ? m_pConnection->getSqlcode() : string();
    }

    std::string SQLConnection::getSqlerr()
    {
        return m_pConnection ? m_pConnection->getSqlerr() : string();
    }  

	std::mutex  SQLConnection::m_lock;
	bool SQLConnection::m_status = false;
	SQLConnection* SQLConnection::m_instance;
	SQLConnection::AutoRelease SQLConnection::m_release;  

	SQLConnection* SQLConnection::GetInstance()
	{
		if (SQLConnection::m_instance==nullptr)
		{
			std::lock_guard<std::mutex> _lock(SQLConnection::m_lock);
			if(SQLConnection::m_instance==nullptr)
			{
				SQLConnection::m_instance = new SQLConnection();
			}
		}
		return SQLConnection::m_instance;	
	};	

	SQLConnection* SQLConnection::GetConnection()
	{
		return SQLConnection::GetInstance();
	}

	int SQLConnection::InitEnv(
		eDBSClient_t dbsClient,
		const string& dbString,
		const string& user,
		const string& pass,
		const string& charset,
		bool encrypt,
		void* pOption)
	{
		if(!SQLConnection::m_status)
		{
			if(!SQLConnection::GetInstance())
			{
				dbslog(DBS_ERROR,"GetInstance Failed!!!");
				return ERR_DBS_FAIL;
			}		
			SQLConnection::GetInstance()->setClientEncode(charset);	
			SQLConnection::GetInstance()->setEncrypt(encrypt);
			SQLConnection::m_status = 
				SQLConnection::GetInstance()->connect(dbsClient,dbString,user,pass,pOption) == ERR_DBS_OK 
				? true : false;
		}
		return SQLConnection::m_status ? ERR_DBS_OK : ERR_DBS_FAIL;
	}

	int  SQLConnection::InitEnv(const DBSConnectionParams& dbsParams)
	{
		eDBSClient_t dbsClient = dbsParams.dbsType;
		string dbstring,user,pass;
		bool encrypt ;
		//topdbs_string::format(dbstring,"%s:%s@%s",
		//	dbsParams.m_dbHost.c_str(),dbsParams.m_dbPort.c_str(),dbsParams.m_dbName.c_str());
		user = dbsParams.dbsUser;
		pass = dbsParams.dbsPass;	
		dbstring = dbsParams.dbsConnStr;
		encrypt = dbsParams.dbsEncrypt;
		return InitEnv(dbsClient,dbstring,user,pass,dbsParams.dbsCharset,encrypt);
	}

	int SQLConnection::InitEnv(const string& dbsfile,const string& dbsgroup)
	{
		if(!SQLConnection::m_status)
		{
			if(!SQLConnection::GetInstance())
			{
				dbslog(DBS_ERROR,"Get dbsInstance Failed!!!");
				return ERR_DBS_FAIL;
			}			
			SQLConnection::m_status = 
				SQLConnection::GetInstance()->connect(dbsfile,dbsgroup) == ERR_DBS_OK 
				? true : false;				
		}
		return SQLConnection::m_status ? ERR_DBS_OK : ERR_DBS_FAIL;		 
	}

	int SQLConnection::UnIntEnv()
	{
		if(SQLConnection::m_instance)
		{
			std::lock_guard<std::mutex> _lock(SQLConnection::m_lock);
			if(SQLConnection::m_instance)
			{
				delete SQLConnection::m_instance;
				SQLConnection::m_instance = nullptr;
			}
		}
		return ERR_DBS_OK;
	}     
}
